#include <netthumb.h>
#include <map>
#include <vector>
#include <string>
#include <sstream>

namespace netthumb {

  /*
    DeviceI
  */

  DeviceI::DeviceI()
  {
    /*
      Valores por defecto para los atributos
      opcionales.
    */

    _linkSt = nullSt;
    _scope = nullScope;
  }

  void
  DeviceI::populate(string addressID,
		    string networkID,
		    string linkID,
		    string location)
  {
    _addressID = addressID;
    _networkID = networkID;
    _linkID = linkID;
    _location = location;
  }

  void
  DeviceI::setLinkSt(LinkSt link)
  {
    _linkSt = link;
  }

  void
  DeviceI::setScope(NetScope scope)
  {
    _scope = scope;
  }

  string
  DeviceI::getAddressID()
  {
    return _addressID;
  }

  string
  DeviceI::getLocation()
  {
    return _location;
  }

  string
  DeviceI::getLinkID()
  {
    return _linkID;
  }

  string
  DeviceI::getNetID()
  {
    return _networkID;
  }

  LinkSt
  DeviceI::getLinkSt()
  {
    return _linkSt;
  }

  NetScope
  DeviceI::getScope()
  {
    return _scope;
  }

  string
  DeviceI::toString()
  {
    map<NetScope, string> scopemap;
    scopemap[nullScope] = string("null");
    scopemap[global] = string("global");
    scopemap[site] = string("site");
    scopemap[link] = string("link");
    scopemap[host] = string("host");

    map<LinkSt, string> linkmap;
    linkmap[nullSt] = string("null");
    linkmap[up] = string("up");
    linkmap[down] = string("down");

    string strOut = string("Location: ");
    strOut.append(_location);
    strOut.append("\n");

    strOut.append("LinkSt: ");
    strOut.append(linkmap[_linkSt]);
    strOut.append("\nAddress ID: ");
    strOut.append(_addressID);
    strOut.append("\nNetwork ID: ");
    strOut.append(_networkID);
    strOut.append("\nLinkID: ");
    strOut.append(_linkID);
    strOut.append("\nScope: ");
    strOut.append(scopemap[_scope]);
    strOut.append("\n");
    return strOut;
  }

  /*
    ConfRouteI
  */

  ConfRouteI::ConfRouteI()
  {
    /*
      Valores por defecto para los
      atributos opcionales
    */
    _viaAddr = "";
    _netType = nullType;
    _metricVal = -1;
  }

  void
  ConfRouteI::populate(string netID,
		       string deviceID,
		       string location)
  {
    _netID = netID;
    _deviceID = deviceID;
    _location = location;
  }

  void
  ConfRouteI::setViaAddr(string viaAddr)
  {
    _viaAddr = viaAddr;
  }

  void
  ConfRouteI::setNetType(NetType netType)
  {
    _netType = netType;
  }

  void
  ConfRouteI::setMetric(int metric)
  {
    _metricVal = metric;
  }

  void
  ConfRouteI::setMetricOrPriority(bool metric)
  {
    _metric = metric;
  }

  bool
  ConfRouteI::getMetricOrPriority()
  {
    return _metric;
  }

  string
  ConfRouteI::getNetID()
  {
    return _netID;
  }

  string
  ConfRouteI::getLocation()
  {
    return _location;
  }

  string
  ConfRouteI::getDeviceID()
  {
    return _deviceID;
  }

  string
  ConfRouteI::getViaAddr()
  {
    return _viaAddr;
  }

  NetType
  ConfRouteI::getNetType()
  {
    return _netType;
  }

  int
  ConfRouteI::getMetric()
  {
    return _metricVal;
  }

  string
  ConfRouteI::toString()
  {
    map<NetType, string> nettypemap;
    nettypemap[nullType] = string("null");
    nettypemap[unicast] = string("unicast");
    nettypemap[unreachable] = string("unreachable");
    nettypemap[blackhole] = string("blackhole");
    nettypemap[prohibit] = string("prohibit");
    nettypemap[local] = string("local");
    nettypemap[broadcast] = string("broadcast");
    nettypemap[thrown] = string("thrown");
    nettypemap[nat] = string("nat");
    nettypemap[anycast] = string("anycast");
    nettypemap[multicast] = string("multicast");

    string strOut = string("Location: ");
    strOut.append(_location);
    strOut.append("\n");

    strOut.append("NetType: ");
    strOut.append(nettypemap[_netType]);
    strOut.append("\nNet ID: ");
    strOut.append(_netID);
    strOut.append("\nDevice ID: ");
    strOut.append(_deviceID);
    strOut.append("\nVia addr: ");
    strOut.append(_viaAddr);
    std::ostringstream buffer;
    buffer << _metricVal;
    if(_metric)
      strOut.append("\nMetric: ");
    else
      strOut.append("\nPriority: ");
    strOut.append(buffer.str());
    strOut.append("\n");
    return strOut;
  }

  /*
    RouteI
  */

  RouteI::RouteI()
  {
    // Valores por defecto para los atributos opcionales.
    _viaAddr = "";
  }

  void
  RouteI::insertConf(ConfRouteI* conf)
  {
    _configurations.push_back(conf);
  }

  void
  RouteI::setLocation(string location)
  {
    _location = location;
  }

  void
  RouteI::setDeviceID(string deviceID)
  {
    _deviceID = deviceID;
  }

  void
  RouteI::setViaAddr(string viaAddr)
  {
    _viaAddr = viaAddr;
  }

  void
  RouteI::setMetric(int metricVal)
  {
    _metricVal = metricVal;
  }

  void
  RouteI::setMetricOrPriority(bool metric)
  {
    _metric = metric;
  }

  string
  RouteI::getDeviceID()
  {
    return _deviceID;
  }

  string
  RouteI::getLocation()
  {
    return _location;
  }

  string
  RouteI::getViaAddr()
  {
    return _viaAddr;
  }

  bool
  RouteI::getMetricOrPriority()
  {
    return _metric;
  }

  int
  RouteI::getMetric()
  {
    return _metricVal;
  }

  vector<ConfRouteI*>&
  RouteI::getConfigurations()
  {
    return _configurations;
  }

  string
  RouteI::toString()
  {

    string strOut = string("Default Options: ");
    strOut.append(_location);
    strOut.append("\n");

    strOut.append("Device ID: ");
    strOut.append(_deviceID);
    strOut.append("\nVia Address: ");
    strOut.append(_viaAddr);
    if(_metric)
      strOut.append("\nMetric: ");
    else
      strOut.append("\nPriority: ");
    std::ostringstream buffer;
    buffer << _metricVal;
    strOut.append(buffer.str());
    strOut.append("\nConfigurations:\n");
    vector<ConfRouteI*>::iterator it;
    for(it = _configurations.begin(); it != _configurations.end(); it++){
      strOut.append((*it)->toString());
    }
    strOut.append("\n");
    return strOut;
  }

  /*
    HostI
  */

  HostI::HostI(){}

  void
  HostI::insertDevice(string name, DeviceI* device){
    _devMap[name] = device;
  }

  void
  HostI::setDevices(map<string, DeviceI*>& devices)
  {
    _devMap = devices;
  }

  void
  HostI::setLocation(string location)
  {
    _location = location;
  }

  void
  HostI::setRoute(RouteI* route)
  {
    _route = route;
  }

  RouteI*
  HostI::getRoute()
  {
    return _route;
  }

  string
  HostI::getLocation()
  {
    return _location;
  }

  map<string, DeviceI*>&
  HostI::getDevices()
  {
    return _devMap;
  }

  string
  HostI::toString()
  {
    string strOut = string("Devices: ");
    strOut.append(_location);
    strOut.append("\n");

    map<string, DeviceI*>::iterator it;
    for(it = _devMap.begin(); it != _devMap.end(); it++){
      strOut.append("Device ");
      strOut.append(it->first);
      strOut.append(":\n");
      strOut.append(it->second->toString());
      strOut.append("\n");
    }
    strOut.append("Route: \n");
    strOut.append(_route->toString());
    strOut.append("\n");
    return strOut;
  }

  /*
    RouterI
  */

  RouterI::RouterI(){}

  void
  RouterI::insertDevice(string name, DeviceI* device){
    _devMap[name] = device;
  }

  void
  RouterI::setLocation(string location)
  {
    _location = location;
  }

  void
  RouterI::setDevices(map<string, DeviceI*>& devices)
  {
    _devMap = devices;
  }

  void
  RouterI::setRoute(RouteI* route)
  {
    _route = route;
  }

  RouteI*
  RouterI::getRoute()
  {
    return _route;
  }

  string
  RouterI::getLocation()
  {
    return _location;
  }

  map<string, DeviceI*>&
  RouterI::getDevices()
  {
    return _devMap;
  }

  string
  RouterI::toString()
  {
    string strOut = string("Devices: ");
    strOut.append(_location);
    strOut.append("\n");

    map<string, DeviceI*>::iterator it;
    for(it = _devMap.begin(); it != _devMap.end(); it++){
      strOut.append("Device ");
      strOut.append(it->first);
      strOut.append(":\n");
      strOut.append(it->second->toString());
      strOut.append("\n");
    }
    strOut.append("Route: \n");
    strOut.append(_route->toString());
    strOut.append("\n");
    return strOut;
  }

  // NetworkI

  NetworkI::NetworkI(){}

  void
  NetworkI::populate(string addressID,
		     string typeID,
		     int mtu,
		     bool multicast,
		     string location)
  {
    _addressID = addressID;

    _mtu = mtu;
    _multicast = multicast;
    _typeID = typeID;
    _location = location;
  }

  string
  NetworkI::getAddress()
  {
    return _addressID;
  }

  string
  NetworkI::getTypeID()
  {
    return _typeID;
  }

  bool
  NetworkI::isMulticast()
  {
    return _multicast;
  }

  string
  NetworkI::getLocation()
  {
    return _location;
  }

  int
  NetworkI::getMtu()
  {
    return _mtu;
  }

  string
  NetworkI::toString()
  {
    string strOut = string("Location: ");
    strOut.append(_location);
    strOut.append("\n");

    strOut.append("AddressID: ");
    strOut.append(_addressID);
    strOut.append("\nType ID: ");
    strOut.append(_typeID);
    strOut.append("\nMTU: ");
    std::ostringstream buffer;
    buffer << _mtu;
    strOut.append(buffer.str());
    strOut.append("\nmulticast: ");
    if(_multicast)
      strOut.append("on\n");
    else
      strOut.append("off\n");

    return strOut;
  }
}
